import {spawn} from "child_process";
import readline from "readline";
import process from "process";

export class AppUtil {
    /**
     * 打开 IntelliJ IDEA
     * @param {string} projectPath 项目路径
     */
    static openIdea(projectPath) {
        const rl = readline.createInterface({
            input: process.stdin,
            output: process.stdout
        });
        rl.question(
            "⚠️ 是否打开IntelliJ IDEA？(y/n) 如果无法打开，请手动打开",
            (answer) => {
                const normalizedAnswer = answer.trim().toLowerCase();
                if (normalizedAnswer === "y" || normalizedAnswer === "yes") {
                    AppUtil.launchIdea(projectPath)
                        .then(() => {
                            console.log("✅ IntelliJ IDEA 已打开:", projectPath);
                        })
                        .catch((error) => {
                            console.error("❌ 打开 IDEA 失败:", error.message);
                            console.info("💡 请确认 IntelliJ IDEA CLI 已配置到 PATH，或设置环境变量 IDEA_BIN 指向 idea 可执行文件。");
                        });
                }
                rl.close();
            }
        );
    }

    /**
     * 使用系统默认浏览器打开指定 URL
     * @param {string} url 目标链接
     * @returns {Promise<void>}
     */
    static openUrl(url) {
        return new Promise((resolve, reject) => {
            if (!url) {
                reject(new Error("URL不能为空"));
                return;
            }
            const platform = process.platform;
            let command;
            let args = [];
            if (platform === "win32") {
                command = "cmd";
                args = ["/c", "start", "", url];
            } else if (platform === "darwin") {
                command = "open";
                args = [url];
            } else {
                command = "xdg-open";
                args = [url];
            }
            try {
                const child = spawn(command, args, {stdio: "ignore"});
                child.once("error", (error) => reject(new Error(`打开浏览器失败: ${error.message}`)));
                child.once("close", (code) => {
                    if (platform === "win32" || code === 0) {
                        resolve();
                    } else {
                        reject(new Error(`打开浏览器失败，退出码 ${code}`));
                    }
                });
            } catch (error) {
                reject(new Error(`打开浏览器失败: ${error.message}`));
            }
        });
    }

    /**
     * 根据操作系统尝试打开 IntelliJ IDEA
     * @param {string} projectPath 项目路径
     * @returns {Promise<void>}
     * @private
     */
    static async launchIdea(projectPath) {
        if (!projectPath) {
            throw new Error("项目路径不能为空");
        }
        const platform = process.platform;

        if (platform === "darwin") {
            await AppUtil.launchIdeaOnMac(projectPath);
            return;
        }
        if (platform === "win32") {
            await AppUtil.launchIdeaOnWindows(projectPath);
            return;
        }
        await AppUtil.launchIdeaOnUnix(projectPath);
    }

    /**
     * Mac 平台：优先尝试 Ultimate 版本，其次社区版
     * @param {string} projectPath
     * @returns {Promise<void>}
     * @private
     */
    static async launchIdeaOnMac(projectPath) {
        const candidates = ["IntelliJ IDEA Ultimate", "IntelliJ IDEA"];
        let lastError = null;
        for (const appName of candidates) {
            try {
                await AppUtil.runCommand("open", ["-a", appName, projectPath]);
                return;
            } catch (error) {
                lastError = error;
            }
        }
        throw lastError || new Error("无法找到可用的 IntelliJ IDEA 应用");
    }

    /**
     * Windows 平台：优先尝试 idea.exe/idea64.exe，其次回落到 start 命令
     * @param {string} projectPath
     * @returns {Promise<void>}
     * @private
     */
    static async launchIdeaOnWindows(projectPath) {
        const envCandidates = [
            process.env.IDEA_BIN,
            process.env.IDEA_PATH,
            process.env.IDEA_EXE
        ].filter(Boolean);
        const binaryCandidates = [...envCandidates, "idea64.exe", "idea.exe"];

        let lastError = null;
        for (const executable of binaryCandidates) {
            try {
                await AppUtil.runCommand(executable, [projectPath], {
                    windowsHide: true,
                    detached: true,
                    resolveOnSpawn: true
                });
                return;
            } catch (error) {
                lastError = error;
            }
        }

        const wrappedCandidates = binaryCandidates.length > 0 ? binaryCandidates : ["idea64.exe"];
        for (const executable of wrappedCandidates) {
            try {
                const command = `start "" ${JSON.stringify(executable)} ${JSON.stringify(projectPath)}`;
                await AppUtil.runCommand(command, [], {
                    shell: true,
                    windowsHide: true,
                    resolveOnSpawn: true
                });
                return;
            } catch (error) {
                lastError = error;
            }
        }

        try {
            // 最后兜底使用资源管理器打开项目目录
            await AppUtil.runCommand(`start "" ${JSON.stringify(projectPath)}`, [], {
                shell: true,
                windowsHide: true,
                resolveOnSpawn: true
            });
        } catch (error) {
            throw lastError || error;
        }
    }

    /**
     * Linux / 其他 Unix 平台：尝试 idea CLI，兜底 xdg-open
     * @param {string} projectPath
     * @returns {Promise<void>}
     * @private
     */
    static async launchIdeaOnUnix(projectPath) {
        const envCandidates = [
            process.env.IDEA_BIN,
            process.env.IDEA_PATH,
            "idea",
            "idea.sh"
        ].filter(Boolean);

        let lastError = null;
        for (const executable of envCandidates) {
            try {
                await AppUtil.runCommand(executable, [projectPath], {
                    detached: true,
                    resolveOnSpawn: true
                });
                return;
            } catch (error) {
                lastError = error;
            }
        }

        try {
            await AppUtil.runCommand("xdg-open", [projectPath]);
        } catch (error) {
            throw lastError || error;
        }
    }

    /**
     * 以 Promise 方式执行子进程
     * @param {string} command 执行命令或脚本
     * @param {string[]} [args] 参数
     * @param {import("child_process").SpawnOptions & {resolveOnSpawn?: boolean, ignoreExitCode?: boolean}} [options] 选项
     * @returns {Promise<void>}
     * @private
     */
    static runCommand(command, args = [], options = {}) {
        const {
            resolveOnSpawn = false,
            ignoreExitCode = false,
            detached = false,
            ...spawnOptions
        } = options;

        return new Promise((resolve, reject) => {
            try {
                const child = spawn(command, args, {
                    stdio: "ignore",
                    detached,
                    ...spawnOptions
                });
                let settled = false;

                const cleanup = () => {
                    child.removeListener("error", handleError);
                    child.removeListener("exit", handleExit);
                    child.removeListener("spawn", handleSpawn);
                };

                const handleError = (error) => {
                    if (settled) return;
                    settled = true;
                    cleanup();
                    reject(error);
                };

                const handleExit = (code) => {
                    if (settled) return;
                    if (ignoreExitCode || code === 0 || code === null) {
                        settled = true;
                        cleanup();
                        resolve();
                    } else if (!resolveOnSpawn) {
                        settled = true;
                        cleanup();
                        reject(new Error(`命令 ${command} 退出码 ${code}`));
                    }
                };

                const handleSpawn = () => {
                    if (settled || !resolveOnSpawn) {
                        return;
                    }
                    settled = true;
                    cleanup();
                    resolve();
                };

                child.once("error", handleError);
                child.once("exit", handleExit);
                if (resolveOnSpawn) {
                    child.once("spawn", handleSpawn);
                }

                if (detached) {
                    child.unref();
                }
            } catch (error) {
                reject(error);
            }
        });
    }
}
